<!DOCTYPE HTML>
<!--
	Dimension by HTML5 UP
	html5up.net | @ajlkn
	Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
-->
<html>
 <head>
  <title>
   Dimension by HTML5 UP
  </title>
  <!-- <meta charset="utf-8" /> -->
  <!-- <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" /> -->
  <meta charset="utf-8"/>
  <meta content="width=device-width,initial-scale=1.0" name="viewport"/>
  <link href="../../assets/css/article.css" rel="stylesheet"/>
  <link href="https://cdn.bootcss.com/highlight.js/9.15.8/styles/github.min.css" rel="stylesheet"/>
  <noscript>
   <link href="../../assets/css/noscript.css" rel="stylesheet"/>
  </noscript>
 </head>
 <body>
  <div id="app">
  </div>
  <!-- built files will be auto injected -->
 </body>
 <body class="is-preload">
  <!-- Wrapper -->
  <div id="wrapper">
   <!-- Main -->
   <div id="main">
    <article id="article">
     <h1 id="soul-dubbo">
      Soul 网关源码解析（四）Dubbo请求概览
     </h1>
     <hr/>
     <h2 id="_1">
      简介
     </h2>
     <p>
      本次启动一个dubbo服务示例，初步探索Soul网关源码的Dubbo请求处理流程
     </p>
     <h2 id="_2">
      概览
     </h2>
     <p>
      接着上篇：
      <a href="https://juejin.cn/post/6917866538712334343">
       Soul网关源码解析（三）请求处理概览
      </a>
     </p>
     <p>
      我们来看一下Dubbo请求的代理转发和HTTP的有什么不一样的地方；经历了那些类；进行了那些操作
     </p>
     <h2 id="_3">
      示例运行
     </h2>
     <h3 id="_4">
      环境配置
     </h3>
     <p>
      在Soul源码clone下来以后，有一个 soul-example 目录，这个就是示例工程，里面有很多的示例可以运行
     </p>
     <p>
      可能初始文件夹不被IDEA识别为Java工程，我们需要点击改工程目录下的 pom.xml 文件，然后在右键，在菜单中选择：add as maven project
     </p>
     <p>
      我们选择运行：soul-examples --&gt; soul-examples-apache-dubbo-service
     </p>
     <p>
      此次示例需要mysql和zookeeper，我们使用docker启动一下,记得修改 soul-admin 的数据库配置，其他的都不需要动，猜测zk有默认配置
     </p>
     <p>
      ```shell script
docker run -dit --name zk -p 2181:2181 zookeepe
docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:latest
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="o">###</span> <span class="err">测试运行</span>
<span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="err">然后运行</span> <span class="n">Soul</span><span class="o">-</span><span class="k">admin</span><span class="err">、</span><span class="n">Soul</span><span class="o">-</span><span class="n">bootstrap</span><span class="p">,</span><span class="err">可以参考：</span><span class="p">[</span><span class="n">Soul网关源码解析</span><span class="err">（二）代码初步运行</span><span class="p">](</span><span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">juejin</span><span class="p">.</span><span class="n">cn</span><span class="o">/</span><span class="n">post</span><span class="o">/</span><span class="mi">6917865804121767944</span><span class="p">)</span>

<span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="err">运行</span><span class="n">soul</span><span class="o">-</span><span class="n">examples</span><span class="o">-</span><span class="n">apache</span><span class="o">-</span><span class="n">dubbo</span><span class="o">-</span><span class="n">service</span>

<span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="err">登录</span> <span class="n">soul</span><span class="o">-</span><span class="k">admin</span> <span class="err">管理界面：</span><span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">localhost</span><span class="p">:</span><span class="mi">9095</span><span class="o">/</span> <span class="err">，账号和密码：</span><span class="k">admin</span> <span class="mi">123456</span>

<span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="err">进入界面：系统管理</span> <span class="c1">--&gt; 插件管理</span>

<span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="err">在插件：</span><span class="n">dubbo</span><span class="err">，点击编辑，将状态修改为开启（是个开关图标）</span>

<span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="o">&amp;</span><span class="n">ensp</span><span class="p">;</span><span class="err">开启成功后，可以在</span><span class="n">Soul</span><span class="o">-</span><span class="n">Bootstrap中看到相关的日志</span><span class="err">，大致如下：</span>

<span class="o">```</span><span class="n">java</span>
<span class="n">o</span><span class="p">.</span><span class="n">d</span><span class="p">.</span><span class="n">s</span><span class="p">.</span><span class="n">p</span><span class="p">.</span><span class="n">a</span><span class="p">.</span><span class="n">d</span><span class="p">.</span><span class="k">c</span><span class="p">.</span><span class="n">ApplicationConfigCache</span>     <span class="p">:</span> <span class="n">init</span> <span class="n">aliaba</span> <span class="n">dubbo</span> <span class="n">reference</span> <span class="n">success</span> <span class="n">there</span> <span class="n">meteData</span> <span class="k">is</span> <span class="p">:</span><span class="n">MetaData</span>
<span class="n">o</span><span class="p">.</span><span class="n">d</span><span class="p">.</span><span class="n">s</span><span class="p">.</span><span class="n">p</span><span class="p">.</span><span class="n">a</span><span class="p">.</span><span class="n">d</span><span class="p">.</span><span class="k">c</span><span class="p">.</span><span class="n">ApplicationConfigCache</span>     <span class="p">:</span> <span class="n">init</span> <span class="n">aliaba</span> <span class="n">dubbo</span> <span class="n">reference</span> <span class="n">success</span> <span class="n">there</span> <span class="n">meteData</span> <span class="k">is</span> <span class="p">:</span><span class="n">MetaData</span>
<span class="n">o</span><span class="p">.</span><span class="n">d</span><span class="p">.</span><span class="n">s</span><span class="p">.</span><span class="n">p</span><span class="p">.</span><span class="n">a</span><span class="p">.</span><span class="n">d</span><span class="p">.</span><span class="k">c</span><span class="p">.</span><span class="n">ApplicationConfigCache</span>     <span class="p">:</span> <span class="n">init</span> <span class="n">aliaba</span> <span class="n">dubbo</span> <span class="n">reference</span> <span class="n">success</span> <span class="n">there</span> <span class="n">meteData</span> <span class="k">is</span> <span class="p">:</span><span class="n">MetaData</span>
</code></pre>
     </div>
     <p>
      进入Soul-admin管理界面：插件管理 --&gt; dubbo ，我们可以看到很多的配置，这些都是soul-examples-apache-dubbo-service的配置
     </p>
     <p>
      随便找个查询的接口：http://localhost:9195/dubbo/findAll
     </p>
     <p>
      使用浏览器访问得到的结果如下：
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="p">{</span>
    <span class="nt">"code"</span><span class="p">:</span> <span class="mi">200</span><span class="p">,</span>
    <span class="nt">"message"</span><span class="p">:</span> <span class="s2">"Access to success!"</span><span class="p">,</span>
    <span class="nt">"data"</span><span class="p">:</span> <span class="p">{</span>
        <span class="nt">"name"</span><span class="p">:</span> <span class="s2">"hello world Soul Apache, findAll"</span><span class="p">,</span>
        <span class="nt">"id"</span><span class="p">:</span> <span class="s2">"-1206394682"</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <p>
      很好，已经成功跑通示例，接下来，进入源码进行debug
     </p>
     <h2 id="_5">
      源码分析
     </h2>
     <p>
      基于上篇，我们已经知道了一个HTTP请求的初步处理流程：
      <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB3-%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86%E6%A6%82%E8%A7%88.md">
       soul源码阅读3-请求处理概览.md
      </a>
     </p>
     <p>
      基于上篇的熟悉度，我们就开始看看Dubbo的处理流程和Http的有什么区别，顺带看一看各个Plugin都干了啥
     </p>
     <p>
      Plugins的链式处理核心类是：
      <strong>
       SoulWebHandler
      </strong>
      ，我们就从这里开始打上断点，逐步进入每个Plugin进行查看
     </p>
     <div class="codehilite">
      <pre><span></span><code>        <span class="kd">public</span> <span class="n">Mono</span><span class="o">&lt;</span><span class="n">Void</span><span class="o">&gt;</span> <span class="nf">execute</span><span class="p">(</span><span class="kd">final</span> <span class="n">ServerWebExchange</span> <span class="n">exchange</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">Mono</span><span class="p">.</span><span class="na">defer</span><span class="p">(()</span> <span class="o">-&gt;</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">index</span> <span class="o">&lt;</span> <span class="n">plugins</span><span class="p">.</span><span class="na">size</span><span class="p">())</span> <span class="p">{</span>
                    <span class="n">SoulPlugin</span> <span class="n">plugin</span> <span class="o">=</span> <span class="n">plugins</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">index</span><span class="o">++</span><span class="p">);</span>
                    <span class="n">Boolean</span> <span class="n">skip</span> <span class="o">=</span> <span class="n">plugin</span><span class="p">.</span><span class="na">skip</span><span class="p">(</span><span class="n">exchange</span><span class="p">);</span>
                    <span class="k">if</span> <span class="p">(</span><span class="n">skip</span><span class="p">)</span> <span class="p">{</span>
                        <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="na">execute</span><span class="p">(</span><span class="n">exchange</span><span class="p">);</span>
                    <span class="p">}</span>
                    <span class="k">return</span> <span class="n">plugin</span><span class="p">.</span><span class="na">execute</span><span class="p">(</span><span class="n">exchange</span><span class="p">,</span> <span class="k">this</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="k">return</span> <span class="n">Mono</span><span class="p">.</span><span class="na">empty</span><span class="p">();</span>
            <span class="p">});</span>
        <span class="p">}</span>
</code></pre>
     </div>
     <h3 id="globalplugin">
      GlobalPlugin
     </h3>
     <p>
      代码看的不是很懂，猜测有下面的功能：
     </p>
     <ul>
      <li>
       获取请求头的 upgrade，此时为null，进入逻辑
      </li>
      <li>
       另外的分支的MultiValueMap有点像文件上传之类需要的，后面有时间验证一下
      </li>
      <li>
       功能是修改了SoulContext
      </li>
     </ul>
     <p>
      这个不是此次分析的目标，到这就跳过
     </p>
     <h3 id="signplugin">
      SignPlugin
     </h3>
     <p>
      这个应该是一个认证相关的插件，但在获取插件信息，没有开启，不进入逻辑，直接跳过，没有进入具体执行
     </p>
     <p>
      这里发现有的能需要进入函数：execute，有的直接进入：doExecute。稍微有点好奇，就看了下原因，大致是直接继承SoulPlugin就直接进入doExecute,而 AbstractSoulPlugin需要进入execute，这里不是重点，就不继续研究这个了，有个大概了解即可
     </p>
     <h3 id="wafpluginratelimiterpluginhystrixpluginresilience4jplugin">
      WafPlugin、RateLimiterPlugin、HystrixPlugin、Resilience4JPlugin
     </h3>
     <p>
      上面几个都是获取插件信息，没有开启，不进入逻辑，直接跳过
     </p>
     <h3 id="divideplugin">
      DividePlugin
     </h3>
     <p>
      这里是true，有点和自己想的不一样，查看管理界面看到divide是开启的，稍微挖一下这个
     </p>
     <p>
      定位到关键函数：Boolean skip = plugin.skip(exchange)，判断是否可以跳过，我们看下具体的代码：
     </p>
     <div class="codehilite">
      <pre><span></span><code>    <span class="kd">public</span> <span class="n">Boolean</span> <span class="nf">skip</span><span class="p">(</span><span class="kd">final</span> <span class="n">ServerWebExchange</span> <span class="n">exchange</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">final</span> <span class="n">SoulContext</span> <span class="n">soulContext</span> <span class="o">=</span> <span class="n">exchange</span><span class="p">.</span><span class="na">getAttribute</span><span class="p">(</span><span class="n">Constants</span><span class="p">.</span><span class="na">CONTEXT</span><span class="p">);</span>
        <span class="k">return</span> <span class="o">!</span><span class="n">Objects</span><span class="p">.</span><span class="na">equals</span><span class="p">(</span><span class="n">Objects</span><span class="p">.</span><span class="na">requireNonNull</span><span class="p">(</span><span class="n">soulContext</span><span class="p">).</span><span class="na">getRpcType</span><span class="p">(),</span> <span class="n">RpcTypeEnum</span><span class="p">.</span><span class="na">HTTP</span><span class="p">.</span><span class="na">getName</span><span class="p">());</span>
    <span class="p">}</span>
</code></pre>
     </div>
     <p>
      好像就是判断类型来返回是否跳过，其他的plugins的判断逻辑，至于这些都是都是怎么来的，今天就不分析了，保留体力，留待下次进行分析
     </p>
     <h3 id="webclientpuluginwebsocketplugin">
      WebClientPulugin、WebsocketPlugin
     </h3>
     <p>
      skip 为 true，直接跳过,跳过的判断的和DividePlugin基本相同，通过类型判断
     </p>
     <h3 id="bodyparamplugin">
      BodyParamPlugin
     </h3>
     <p>
      对soulContext进行了操作，和GlobalPlugin有一些联动，还看到了MediaType等关键字，感觉很像文件上传之类的，但细节不太清楚，这个也不是本次分析的目的，不要陷入细节，这次放过它，下次有时间再仔细研究
     </p>
     <h3 id="alibabadubbleplugin">
      AlibabaDubblePlugin
     </h3>
     <p>
      这个插件是本次的核心，大致干了下面三个事情
     </p>
     <ul>
      <li>
       进入了并且匹配上了规则，发现这个plugin是dubbo总插件，同样能处理Apache dubbo，也就是他们是相同的或者复用的
      </li>
      <li>
       进入到doExecute函数：获取body，soulContext，查看soulContext发现已经有方法、路径等信息（猜测是前面加的），获得关键的metaData
      </li>
      <li>
       获取请求的结果，并且放到exchange中：Object result = alibabaDubboProxyService.genericInvoker(body, metaData)
      </li>
     </ul>
     <p>
      代码大致如下：
     </p>
     <div class="codehilite">
      <pre><span></span><code>    <span class="kd">protected</span> <span class="n">Mono</span><span class="o">&lt;</span><span class="n">Void</span><span class="o">&gt;</span> <span class="nf">doExecute</span><span class="p">(</span><span class="kd">final</span> <span class="n">ServerWebExchange</span> <span class="n">exchange</span><span class="p">,</span> <span class="kd">final</span> <span class="n">SoulPluginChain</span> <span class="n">chain</span><span class="p">,</span> <span class="kd">final</span> <span class="n">SelectorData</span> <span class="n">selector</span><span class="p">,</span> <span class="kd">final</span> <span class="n">RuleData</span> <span class="n">rule</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">String</span> <span class="n">body</span> <span class="o">=</span> <span class="n">exchange</span><span class="p">.</span><span class="na">getAttribute</span><span class="p">(</span><span class="n">Constants</span><span class="p">.</span><span class="na">DUBBO_PARAMS</span><span class="p">);</span>
        <span class="n">SoulContext</span> <span class="n">soulContext</span> <span class="o">=</span> <span class="n">exchange</span><span class="p">.</span><span class="na">getAttribute</span><span class="p">(</span><span class="n">Constants</span><span class="p">.</span><span class="na">CONTEXT</span><span class="p">);</span>
        <span class="k">assert</span> <span class="n">soulContext</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">;</span>
        <span class="c1">// dubbo 请求的相关数据</span>
        <span class="n">MetaData</span> <span class="n">metaData</span> <span class="o">=</span> <span class="n">exchange</span><span class="p">.</span><span class="na">getAttribute</span><span class="p">(</span><span class="n">Constants</span><span class="p">.</span><span class="na">META_DATA</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">checkMetaData</span><span class="p">(</span><span class="n">metaData</span><span class="p">))</span> <span class="p">{</span>
            <span class="k">assert</span> <span class="n">metaData</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">;</span>
            <span class="n">log</span><span class="p">.</span><span class="na">error</span><span class="p">(</span><span class="s">" path is :{}, meta data have error.... {}"</span><span class="p">,</span> <span class="n">soulContext</span><span class="p">.</span><span class="na">getPath</span><span class="p">(),</span> <span class="n">metaData</span><span class="p">.</span><span class="na">toString</span><span class="p">());</span>
            <span class="n">exchange</span><span class="p">.</span><span class="na">getResponse</span><span class="p">().</span><span class="na">setStatusCode</span><span class="p">(</span><span class="n">HttpStatus</span><span class="p">.</span><span class="na">INTERNAL_SERVER_ERROR</span><span class="p">);</span>
            <span class="n">Object</span> <span class="n">error</span> <span class="o">=</span> <span class="n">SoulResultWrap</span><span class="p">.</span><span class="na">error</span><span class="p">(</span><span class="n">SoulResultEnum</span><span class="p">.</span><span class="na">META_DATA_ERROR</span><span class="p">.</span><span class="na">getCode</span><span class="p">(),</span> <span class="n">SoulResultEnum</span><span class="p">.</span><span class="na">META_DATA_ERROR</span><span class="p">.</span><span class="na">getMsg</span><span class="p">(),</span> <span class="kc">null</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">WebFluxResultUtils</span><span class="p">.</span><span class="na">result</span><span class="p">(</span><span class="n">exchange</span><span class="p">,</span> <span class="n">error</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">StringUtils</span><span class="p">.</span><span class="na">isNoneBlank</span><span class="p">(</span><span class="n">metaData</span><span class="p">.</span><span class="na">getParameterTypes</span><span class="p">())</span> <span class="o">&amp;&amp;</span> <span class="n">StringUtils</span><span class="p">.</span><span class="na">isBlank</span><span class="p">(</span><span class="n">body</span><span class="p">))</span> <span class="p">{</span>
            <span class="n">exchange</span><span class="p">.</span><span class="na">getResponse</span><span class="p">().</span><span class="na">setStatusCode</span><span class="p">(</span><span class="n">HttpStatus</span><span class="p">.</span><span class="na">INTERNAL_SERVER_ERROR</span><span class="p">);</span>
            <span class="n">Object</span> <span class="n">error</span> <span class="o">=</span> <span class="n">SoulResultWrap</span><span class="p">.</span><span class="na">error</span><span class="p">(</span><span class="n">SoulResultEnum</span><span class="p">.</span><span class="na">DUBBO_HAVE_BODY_PARAM</span><span class="p">.</span><span class="na">getCode</span><span class="p">(),</span> <span class="n">SoulResultEnum</span><span class="p">.</span><span class="na">DUBBO_HAVE_BODY_PARAM</span><span class="p">.</span><span class="na">getMsg</span><span class="p">(),</span> <span class="kc">null</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">WebFluxResultUtils</span><span class="p">.</span><span class="na">result</span><span class="p">(</span><span class="n">exchange</span><span class="p">,</span> <span class="n">error</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="c1">// 获取请求结果</span>
        <span class="n">Object</span> <span class="n">result</span> <span class="o">=</span> <span class="n">alibabaDubboProxyService</span><span class="p">.</span><span class="na">genericInvoker</span><span class="p">(</span><span class="n">body</span><span class="p">,</span> <span class="n">metaData</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">Objects</span><span class="p">.</span><span class="na">nonNull</span><span class="p">(</span><span class="n">result</span><span class="p">))</span> <span class="p">{</span>
            <span class="c1">// 将结果放到exchange中</span>
            <span class="n">exchange</span><span class="p">.</span><span class="na">getAttributes</span><span class="p">().</span><span class="na">put</span><span class="p">(</span><span class="n">Constants</span><span class="p">.</span><span class="na">DUBBO_RPC_RESULT</span><span class="p">,</span> <span class="n">result</span><span class="p">);</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">exchange</span><span class="p">.</span><span class="na">getAttributes</span><span class="p">().</span><span class="na">put</span><span class="p">(</span><span class="n">Constants</span><span class="p">.</span><span class="na">DUBBO_RPC_RESULT</span><span class="p">,</span> <span class="n">Constants</span><span class="p">.</span><span class="na">DUBBO_RPC_RESULT_EMPTY</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="n">exchange</span><span class="p">.</span><span class="na">getAttributes</span><span class="p">().</span><span class="na">put</span><span class="p">(</span><span class="n">Constants</span><span class="p">.</span><span class="na">CLIENT_RESPONSE_RESULT_TYPE</span><span class="p">,</span> <span class="n">ResultEnum</span><span class="p">.</span><span class="na">SUCCESS</span><span class="p">.</span><span class="na">getName</span><span class="p">());</span>
        <span class="k">return</span> <span class="n">chain</span><span class="p">.</span><span class="na">execute</span><span class="p">(</span><span class="n">exchange</span><span class="p">);</span>
    <span class="p">}</span>
</code></pre>
     </div>
     <p>
      继续跟踪查看获取result的那个函数，通过下面的代码可以明显看出确实是rpc调用，并得到结果
     </p>
     <div class="codehilite">
      <pre><span></span><code>    <span class="kd">public</span> <span class="n">Object</span> <span class="nf">genericInvoker</span><span class="p">(</span><span class="kd">final</span> <span class="n">String</span> <span class="n">body</span><span class="p">,</span> <span class="kd">final</span> <span class="n">MetaData</span> <span class="n">metaData</span><span class="p">)</span> <span class="kd">throws</span> <span class="n">SoulException</span> <span class="p">{</span>
        <span class="c1">// 通过rpc和dubbo的相关知识，这里的reference相当于consumer，metaData.getPath()==/dubbo/findAll</span>
        <span class="c1">// ApplicationConfigCache.getInstance().get 的逻辑感觉比较复杂，先不看了，大体感觉是初始化的时候使用 path 作为了 key</span>
        <span class="n">ReferenceConfig</span><span class="o">&lt;</span><span class="n">GenericService</span><span class="o">&gt;</span> <span class="n">reference</span> <span class="o">=</span> <span class="n">ApplicationConfigCache</span><span class="p">.</span><span class="na">getInstance</span><span class="p">().</span><span class="na">get</span><span class="p">(</span><span class="n">metaData</span><span class="p">.</span><span class="na">getPath</span><span class="p">());</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">Objects</span><span class="p">.</span><span class="na">isNull</span><span class="p">(</span><span class="n">reference</span><span class="p">)</span> <span class="o">||</span> <span class="n">StringUtils</span><span class="p">.</span><span class="na">isEmpty</span><span class="p">(</span><span class="n">reference</span><span class="p">.</span><span class="na">getInterface</span><span class="p">()))</span> <span class="p">{</span>
            <span class="n">ApplicationConfigCache</span><span class="p">.</span><span class="na">getInstance</span><span class="p">().</span><span class="na">invalidate</span><span class="p">(</span><span class="n">metaData</span><span class="p">.</span><span class="na">getPath</span><span class="p">());</span>
            <span class="n">reference</span> <span class="o">=</span> <span class="n">ApplicationConfigCache</span><span class="p">.</span><span class="na">getInstance</span><span class="p">().</span><span class="na">initRef</span><span class="p">(</span><span class="n">metaData</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="c1">// 这段很像RPC Demo中的获取字节码生成的对象</span>
        <span class="n">GenericService</span> <span class="n">genericService</span> <span class="o">=</span> <span class="n">reference</span><span class="p">.</span><span class="na">get</span><span class="p">();</span>
        <span class="k">try</span> <span class="p">{</span>
            <span class="n">Pair</span><span class="o">&lt;</span><span class="n">String</span><span class="o">[]</span><span class="p">,</span> <span class="n">Object</span><span class="o">[]&gt;</span> <span class="n">pair</span><span class="p">;</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">ParamCheckUtils</span><span class="p">.</span><span class="na">dubboBodyIsEmpty</span><span class="p">(</span><span class="n">body</span><span class="p">))</span> <span class="p">{</span>
                <span class="n">pair</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ImmutablePair</span><span class="o">&lt;&gt;</span><span class="p">(</span><span class="k">new</span> <span class="n">String</span><span class="o">[]</span><span class="p">{},</span> <span class="k">new</span> <span class="n">Object</span><span class="o">[]</span><span class="p">{});</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="n">pair</span> <span class="o">=</span> <span class="n">dubboParamResolveService</span><span class="p">.</span><span class="na">buildParameter</span><span class="p">(</span><span class="n">body</span><span class="p">,</span> <span class="n">metaData</span><span class="p">.</span><span class="na">getParameterTypes</span><span class="p">());</span>
            <span class="p">}</span>
            <span class="c1">// 传入方法名、参数名、参数？Dubbo还没用的熟练，暂时这样猜一猜，不影响此次分析的大局</span>
            <span class="k">return</span> <span class="n">genericService</span><span class="p">.</span><span class="na">$invoke</span><span class="p">(</span><span class="n">metaData</span><span class="p">.</span><span class="na">getMethodName</span><span class="p">(),</span> <span class="n">pair</span><span class="p">.</span><span class="na">getLeft</span><span class="p">(),</span> <span class="n">pair</span><span class="p">.</span><span class="na">getRight</span><span class="p">());</span>
        <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">GenericException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">log</span><span class="p">.</span><span class="na">error</span><span class="p">(</span><span class="s">"dubbo invoker have exception"</span><span class="p">,</span> <span class="n">e</span><span class="p">);</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="n">SoulException</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="na">getExceptionMessage</span><span class="p">());</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre>
     </div>
     <p>
      对reference.get()，有点好奇，稍微看下 ReferenceConfig
     </p>
     <div class="codehilite">
      <pre><span></span><code>    <span class="kd">public</span> <span class="kd">synchronized</span> <span class="n">T</span> <span class="nf">get</span><span class="p">()</span> <span class="p">{</span>
        <span class="c1">// 判断是否能用</span>
        <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">destroyed</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="n">IllegalStateException</span><span class="p">(</span><span class="s">"Already destroyed!"</span><span class="p">);</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="c1">// 有延迟加载的作用</span>
            <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">ref</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">this</span><span class="p">.</span><span class="na">init</span><span class="p">();</span>
            <span class="p">}</span>

            <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="na">ref</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre>
     </div>
     <p>
      看到它这用法还是挺巧妙的，有一个延迟加载的效果，好像可以按照这个思路去改一改自己的RPC Demo的客户端代理生成，哈哈
     </p>
     <h3 id="monitorplugin">
      MonitorPlugin
     </h3>
     <p>
      获取插件信息，没有开启，不进入逻辑，直接跳过
     </p>
     <h3 id="webclientresponseplugin">
      WebClientResponsePlugin
     </h3>
     <p>
      skip = true，直接跳过
     </p>
     <h3 id="dubboresponseplugin">
      DubboResponsePlugin
     </h3>
     <p>
      看名字就知道是个核心类，我们在then后执行的代码段打个断点（这种需要单独打个端口，后面才能跳进去）
     </p>
     <div class="codehilite">
      <pre><span></span><code>    <span class="kd">public</span> <span class="n">Mono</span><span class="o">&lt;</span><span class="n">Void</span><span class="o">&gt;</span> <span class="nf">execute</span><span class="p">(</span><span class="kd">final</span> <span class="n">ServerWebExchange</span> <span class="n">exchange</span><span class="p">,</span> <span class="kd">final</span> <span class="n">SoulPluginChain</span> <span class="n">chain</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">chain</span><span class="p">.</span><span class="na">execute</span><span class="p">(</span><span class="n">exchange</span><span class="p">).</span><span class="na">then</span><span class="p">(</span><span class="n">Mono</span><span class="p">.</span><span class="na">defer</span><span class="p">(()</span> <span class="o">-&gt;</span> <span class="p">{</span>
            <span class="c1">// 从exchange中拿到结果</span>
            <span class="kd">final</span> <span class="n">Object</span> <span class="n">result</span> <span class="o">=</span> <span class="n">exchange</span><span class="p">.</span><span class="na">getAttribute</span><span class="p">(</span><span class="n">Constants</span><span class="p">.</span><span class="na">DUBBO_RPC_RESULT</span><span class="p">);</span>
            <span class="k">try</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">Objects</span><span class="p">.</span><span class="na">isNull</span><span class="p">(</span><span class="n">result</span><span class="p">))</span> <span class="p">{</span>
                    <span class="n">Object</span> <span class="n">error</span> <span class="o">=</span> <span class="n">SoulResultWrap</span><span class="p">.</span><span class="na">error</span><span class="p">(</span><span class="n">SoulResultEnum</span><span class="p">.</span><span class="na">SERVICE_RESULT_ERROR</span><span class="p">.</span><span class="na">getCode</span><span class="p">(),</span> <span class="n">SoulResultEnum</span><span class="p">.</span><span class="na">SERVICE_RESULT_ERROR</span><span class="p">.</span><span class="na">getMsg</span><span class="p">(),</span> <span class="kc">null</span><span class="p">);</span>
                    <span class="k">return</span> <span class="n">WebFluxResultUtils</span><span class="p">.</span><span class="na">result</span><span class="p">(</span><span class="n">exchange</span><span class="p">,</span> <span class="n">error</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="n">Object</span> <span class="n">success</span> <span class="o">=</span> <span class="n">SoulResultWrap</span><span class="p">.</span><span class="na">success</span><span class="p">(</span><span class="n">SoulResultEnum</span><span class="p">.</span><span class="na">SUCCESS</span><span class="p">.</span><span class="na">getCode</span><span class="p">(),</span> <span class="n">SoulResultEnum</span><span class="p">.</span><span class="na">SUCCESS</span><span class="p">.</span><span class="na">getMsg</span><span class="p">(),</span> <span class="n">JsonUtils</span><span class="p">.</span><span class="na">removeClass</span><span class="p">(</span><span class="n">result</span><span class="p">));</span>
                <span class="c1">// 进入后使用之前熟悉的：exchange.getResponse().writeWith，返回响应给客户端</span>
                <span class="k">return</span> <span class="n">WebFluxResultUtils</span><span class="p">.</span><span class="na">result</span><span class="p">(</span><span class="n">exchange</span><span class="p">,</span> <span class="n">success</span><span class="p">);</span>
            <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">SoulException</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">return</span> <span class="n">Mono</span><span class="p">.</span><span class="na">empty</span><span class="p">();</span>
            <span class="p">}</span>
        <span class="p">}));</span>
    <span class="p">}</span>
</code></pre>
     </div>
     <p>
      执行完plugins链后进入到这个断点：大致是获取结果result，加工得到success
     </p>
     <p>
      然后WebFluxResultUtils.result(exchange, success)，进入后是：exchange.getResponse().writeWith，返回响应给客户端
     </p>
     <p>
      到这里，一个Dubbo请求的处理流程大致展示在我们面前了，有了前面几篇的分析基础，还是挺流畅的
     </p>
     <h2 id="_6">
      总结
     </h2>
     <p>
      此次分析验证了在上篇中的一些猜想，也得到了一些新的东西，请求处理的大致流程图如下：
     </p>
     <p>
      <img alt="" src="./picture/Soulprocessfirst.png"/>
     </p>
     <ul>
      <li>
       HttpServerOperations : 明显的netty的请求接收的地方，请求入口
      </li>
      <li>
       ReactorHttpHandlerAdapter ：生成response和request
      </li>
      <li>
       HttpWebHandlerAdapter ：exchange 的生成
      </li>
      <li>
       FilteringWebHandler : filter 操作
      </li>
      <li>
       SoulWebHandler ：plugins调用
      </li>
     </ul>
     <p>
      可以参考下上篇分析：
      <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB3-%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86%E6%A6%82%E8%A7%88.md">
       Soul 源码阅读（三）HTTP请求处理概览
      </a>
     </p>
     <p>
      这篇中我们详细分析各个Dubbo 经过的 Plugin 的相关处理，有些复杂的就没有看了，但也得到我们此次分析想要的结果，脑海中对一个RPC请求如果进行处理，设计那些组件有了一定的了解，基于这些了解也能进行更近一步的分析
     </p>
     <p>
      我们收获和HTTP请求调用、RPC请求调用、Websocket请求调用是互斥的，通过判断请求的类型来进行选择其中一个，而HTTP相应、RPC响应都会转为HTTP响应，然后返回给客户端，exchange里面估计是绑定了一个socket，然后直接调用即可（Netty网关Demo又可以进行借鉴了）
     </p>
     <p>
      此次分析有了下面新的疑问：
     </p>
     <ul>
      <li>
       Websocket的响应的返回时怎样的？是长连接吗？因为没有看到Websocket响应相关的处理类
      </li>
      <li>
       exchange中的请求类型是怎么绑定，如何生成的？
      </li>
      <li>
       限流等插件也是需要进行匹配吗？具体执行逻辑是怎样的？
      </li>
     </ul>
     <p>
      上面这些问题就留待以后分析了，慢慢来才能可持续发展
     </p>
     <h2 id="soul">
      Soul网关源码解析文章列表
     </h2>
     <h3 id="_7">
      掘金
     </h3>
     <h4 id="_8">
      了解与初步运行
     </h4>
     <ul>
      <li>
       <a href="https://juejin.cn/post/6917864624423436296">
        Soul网关源码解析（一） 概览
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6917865804121767944">
        Soul网关源码解析（二）代码初步运行
       </a>
      </li>
     </ul>
     <h4 id="_9">
      请求处理流程解析
     </h4>
     <ul>
      <li>
       <a href="https://juejin.cn/post/6917866538712334343">
        Soul网关源码解析（三）请求处理概览
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6917867369909977102">
        Soul网关源码解析（四）Dubbo请求概览
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6918575905962983438">
        Soul网关源码解析（五）请求类型探索
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6918736260467015693">
        Soul网关源码解析（六）Sofa请求处理概览
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6919348164944232455/">
        Soul网关源码解析（七）限流插件初探
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6919774553241550855/">
        Soul网关源码解析（八）路由匹配初探
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6920074307590684685/">
        Soul网关源码解析（九）插件配置加载初探
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6920142348617777166">
        Soul网关源码解析（十）自定义简单插件编写
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6920596034171174925">
        Soul网关源码解析（十一）请求处理小结
       </a>
      </li>
     </ul>
     <h4 id="_10">
      数据同步解析
     </h4>
     <ul>
      <li>
       <a href="https://juejin.cn/post/6920596173925384206">
        Soul网关源码解析（十二）数据同步初探
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6920596028505178125">
        Soul网关源码解析（十三）Websocket同步数据-Bootstrap端
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6920597298674302983">
        Soul网关源码解析（十四）HTTP数据同步-Bootstrap端
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6920764643967238151">
        Soul网关源码解析（十五）Zookeeper数据同步-Bootstrap端
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6921170233868845064">
        Soul网关源码解析（十六）Nacos数据同步示例运行
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6921325882753695757/">
        Soul网关源码解析（十七）Nacos数据同步解析-Bootstrap端
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6921495273122463751/">
        Soul网关源码解析（十八）Zookeeper数据同步初探-Admin端
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6921621915995996168/">
        Soul网关源码解析（十九）Nacos数据同步初始化修复-Admin端
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6921988280187617287/">
        Soul网关源码解析（二十）Websocket数据同步-Admin端
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6922301585288593416/">
        Soul网关源码解析（二十一）HTTP长轮询数据同步-Admin端
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6922584596810825735/">
        Soul网关源码解析（二十二）数据同步小结
       </a>
      </li>
     </ul>
     <h4 id="soul-client">
      Soul-Client模块
     </h4>
     <ul>
      <li>
       <a href="https://juejin.cn/post/6922643958455599111">
        Soul网关源码解析（二十三）SoulSpringMvcClient注解
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6922722161702469640/">
        Soul网关源码解析（二十四）SoulDubboClient注解
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6922745260435046408/">
        Soul网关源码解析（二十五）Soul-Client模块小结
       </a>
      </li>
     </ul>
     <h4 id="_11">
      总览
     </h4>
     <ul>
      <li>
       <a href="https://juejin.cn/post/6922960265663217678/">
        Soul网关源码解析（二十六）初步总览
       </a>
      </li>
     </ul>
     <h4 id="_12">
      番外
     </h4>
     <ul>
      <li>
       <a href="https://juejin.cn/post/6918947689564471309">
        Soul网关源码阅读番外篇（一） HTTP参数请求错误
       </a>
      </li>
     </ul>
    </article>
   </div>
   <!-- Footer -->
   <footer id="footer">
    <p class="copyright">
     © Untitled. Design:
     <a href="https://html5up.net">
      HTML5 UP
     </a>
     .
    </p>
   </footer>
  </div>
  <!-- BG -->
  <div id="bg">
  </div>
  <!-- Scripts -->
  <script src="../assets/js/jquery.min.js">
  </script>
  <script src="../assets/js/browser.min.js">
  </script>
  <script src="../assets/js/breakpoints.min.js">
  </script>
  <script src="../assets/js/util.js">
  </script>
  <script src="../assets/js/main.js">
  </script>
 </body>
</html>
